// froala-editor 此配置来自https://github.com/froala/vue-froala-wysiwyg/
import FroalaEditor from 'froala-editor';
const VueFroala = (Vue, Options = {}) => {

  let froalaEditorFunctionality = {

    props: ['tag', 'value', 'config', 'onManualControllerReady'],

    watch: {
      value: function () {
        this.model = this.value;
        this.updateValue();
      }
    },

    render: function (createElement) {
      return createElement(
          this.currentTag,
          [this.$slots.default]
      )
    },

    created: function () {
      this.currentTag = this.tag || this.currentTag;
      this.model = this.value;
    },

    // After first time render.
    mounted: function() {
      if (this.SPECIAL_TAGS.indexOf(this.currentTag) != -1) {

        this.hasSpecialTag = true;
      }

      if (this.onManualControllerReady) {
        this.generateManualController();
      } else {
        this.createEditor();
      }
    },

    beforeDestroy: function() {
      this.destroyEditor();
    },

    data: function () {

      return {

        initEvents: [],

        // Tag on which the editor is initialized.
        currentTag: 'div',

        // Editor element.
        _editor: null,

        // Current config.
        currentConfig: null,

        // Editor options config
        defaultConfig: {
          immediateVueModelUpdate: false,
          vueIgnoreAttrs: null
        },

        editorInitialized: false,

        SPECIAL_TAGS: ['img', 'button', 'input', 'a'],
        INNER_HTML_ATTR: 'innerHTML',
        hasSpecialTag: false,

        model: null,
        oldModel: null
      };
    },
    methods: {
      updateValue: function() {
        if (JSON.stringify(this.oldModel) == JSON.stringify(this.model)) {
          return;
        }

        this.setContent();
      },

      createEditor: function() {

        if (this.editorInitialized) {
          return;
        }

        this.currentConfig = this.clone(this.config || this.defaultConfig);

        this.setContent(true);

        // Bind editor events.
        this.registerEvents();
        this.initListeners();

        this._editor = new FroalaEditor(this.$el, this.currentConfig)

        this.editorInitialized = true;

      },

      // Return clone object
      clone(item) {
        const me = this;
        if (!item) {
          return item;
        } // null, undefined values check

        let types = [Number, String, Boolean],
            result;

        // normalizing primitives if someone did new String('aaa'), or new Number('444');
        types.forEach(function (type) {
          if (item instanceof type) {
            result = type(item);
          }
        });

        if (typeof result == "undefined") {
          if (Object.prototype.toString.call(item) === "[object Array]") {
            result = [];
            item.forEach(function (child, index, array) {
              result[index] = me.clone(child);
            });
          } else if (typeof item == "object") {
            // testing that this is DOM
            if (item.nodeType && typeof item.cloneNode == "function") {
              result = item.cloneNode(true);
            } else if (!item.prototype) { // check that this is a literal
              if (item instanceof Date) {
                result = new Date(item);
              } else {
                // it is an object literal
                result = {};
                for (let i in item) {
                  result[i] = me.clone(item[i]);
                }
              }
            } else {
              // eslint-disable-next-line no-constant-condition
              if (false && item.constructor) {
                result = new item.constructor();
              } else {
                result = item;
              }
            }
          } else {
            result = item;
          }
        }
        return result;
      },

      setContent: function (firstTime) {

        if (!this.editorInitialized && !firstTime) {
          return;
        }

        if (this.model || this.model == '') {

          this.oldModel = this.model;

          if (this.hasSpecialTag) {
            this.setSpecialTagContent();
          } else {
            this.setNormalTagContent(firstTime);
          }
        }
      },

      setNormalTagContent: function(firstTime) {

        let self = this;

        function htmlSet() {

          // Check if editor not null
          if (self._editor == null) {
            return;
          }

          if (self._editor.html != undefined) {
            self._editor.html.set(self.model || '');
          }

          //This will reset the undo stack everytime the model changes externally. Can we fix this?

          if (self._editor.undo != undefined) {
            self._editor.undo.saveStep();
            self._editor.undo.reset();
          }

        }

        if (firstTime) {
          this.registerEvent('initialized', function () {
            htmlSet();
          });
        } else {
          htmlSet();
        }

      },

      setSpecialTagContent: function() {

        let tags = this.model;

        // add tags on element
        if (tags) {

          for (let attr in tags) {
            // eslint-disable-next-line no-prototype-builtins
            if (tags.hasOwnProperty(attr) && attr != this.INNER_HTML_ATTR) {
              this.$el.setAttribute(attr, tags[attr]);
            }
          }

          // eslint-disable-next-line no-prototype-builtins
          if (tags.hasOwnProperty(this.INNER_HTML_ATTR)) {
            this.$el.innerHTML = tags[this.INNER_HTML_ATTR];
          }
        }
      },

      destroyEditor: function() {
        this.initEvents = [];

        if (this._editor) {

          this._editor.destroy();
          this.editorInitialized = false;
          this._editor = null;
        }
      },

      getEditor: function() {
        return this._editor;
      },

      generateManualController: function() {
        let controls = {
          initialize: this.createEditor,
          destroy: this.destroyEditor,
          getEditor: this.getEditor,
        };

        this.onManualControllerReady(controls);
      },

      updateModel: function () {

        let modelContent = '';

        if (this.hasSpecialTag) {

          let attributeNodes = this.$el[0].attributes;
          let attrs = {};

          for (let i = 0; i < attributeNodes.length; i++ ) {

            let attrName = attributeNodes[i].name;
            if (this.currentConfig.vueIgnoreAttrs && this.currentConfig.vueIgnoreAttrs.indexOf(attrName) != -1) {
              continue;
            }
            attrs[attrName] = attributeNodes[i].value;
          }

          if (this.$el[0].innerHTML) {
            attrs[this.INNER_HTML_ATTR] = this.$el[0].innerHTML;
          }

          modelContent = attrs;
        } else {

          let returnedHtml = this._editor.html.get();
          if (typeof returnedHtml === 'string') {
            modelContent = returnedHtml;
          }
        }

        this.oldModel = modelContent;
        this.$emit('input', modelContent);
      },

      initListeners: function() {
        let self = this;

        this.registerEvent('initialized', function () {

          // Editor initialized
          self.editorInitialized = true;

          // Check if editor not null and editor has events
          if (self._editor != null && self._editor.events) {
            // bind contentChange and keyup event to froalaModel
            self._editor.events.on('contentChanged', function () {
              self.updateModel();
            });

            if (self.currentConfig.immediateVueModelUpdate) {
              self._editor.events.on('keyup', function () {
                self.updateModel();
              });
            }
          }
        })
      },

      // register event on editor element
      registerEvent: function (eventName, callback) {

        if (!eventName || !callback) {
          return;
        }

        // Initialized event.
        if (eventName == 'initialized') {

          this.initEvents.push(callback);
        }
        else {
          if (!this.currentConfig.events) {
            this.currentConfig.events = {};
          }

          this.currentConfig.events[eventName] = callback;
        }

      },

      registerEvents: function () {
        // Handle initialized on its own.
        this.registerInitialized();

        // Get current events.
        let events = this.currentConfig.events;

        if (!events) {
          return;
        }

        for (let event in events) {
          // eslint-disable-next-line no-prototype-builtins
          if (events.hasOwnProperty(event) && event != 'initialized') {
            this.registerEvent(event, events[event]);
          }
        }
      },

      registerInitialized: function () {
        // Bind initialized.
        if(!this.currentConfig.events) {
          this.currentConfig.events = {};
        }

        // Set original initialized event.
        if (this.currentConfig.events.initialized) {
          this.registerEvent('initialized', this.currentConfig.events.initialized);
        }

        // Bind initialized event.
        this.currentConfig.events.initialized = () => {
          for (let i = 0; i < this.initEvents.length; i++) {
            this.initEvents[i].call(this._editor);
          }
        }
      }
    }
  };

  Vue.component('Froala', froalaEditorFunctionality);

  let froalaViewFunctionality = {

    props: ['tag', 'value'],

    watch: {
      value: function (newValue) {
        this._element.innerHTML = newValue;
      }
    },

    created: function () {
      this.currentTag = this.tag || this.currentTag;
    },

    render: function (createElement) {
      return createElement(
          this.currentTag,
          {
            class: 'fr-view'
          }
      )
    },

    // After first time render.
    mounted: function() {
      this._element = this.$el;

      if (this.value) {
        this._element.innerHTML = this.value
      }
    },

    data: function () {

      return {
        currentTag: 'div',
        _element: null,
      };
    }
  };

  Vue.component('FroalaView', froalaViewFunctionality);
}
export default VueFroala